﻿using System;
using System.Collections.Generic;
using System.Linq;

namespace VSTalk.Engine.Core.Feed
{
    public class MessageFeed : IMessageFeed
    {
        private readonly IList<IMessageConsumer> _consumers = new List<IMessageConsumer>();
        private readonly IList<IMessageProvider> _providers = new List<IMessageProvider>();
        private readonly IList<IMessage> _messages = new List<IMessage>();

        public void Subscribe(IMessageConsumer consumer)
        {
            if (_consumers.Contains(consumer))
            {
                return;
            }
            _consumers.Add(consumer);
        }

        public void Unsubscribe(IMessageConsumer consumer)
        {
            if (!_consumers.Contains(consumer))
            {
                return;
            }
            _consumers.Remove(consumer);
        }

        public void RegisterProvider(IMessageProvider provider)
        {
            if (_providers.Contains(provider))
            {
                return;
            }
            _providers.Add(provider);
            StartListen(provider);
        }

        private void StartListen(IMessageProvider provider)
        {
            provider.MessageReceived += ProviderOnMessageReceived;
        }

        private void ProviderOnMessageReceived(object sender, MessageReceivedArgs args)
        {
            _messages.Add(args.Message);
            foreach (var messageConsumer in _consumers.ToList())
            {
                if (messageConsumer.Predicate(args.Message))
                {
                    messageConsumer.Consume(args.Message);
                }
            }
        }

        public void UnregisterProvider(IMessageProvider provider)
        {
            if (!_providers.Contains(provider))
            {
                return;
            }
            StopListen(provider);
            _providers.Remove(provider);
        }

        public IEnumerable<IMessage> GetMessages(Predicate<IMessage> predicate)
        {
            return _messages.Where(message => predicate(message));
        }

        private void StopListen(IMessageProvider provider)
        {
            provider.MessageReceived -= ProviderOnMessageReceived;
        }
    }
}